#include <iostream>
#include <sstream>
#include <string.h>
#include <set>
#include "Uci.h"
#include "DebugTest.h"
#include "DebugBench.h"
#include "Move.h"
using namespace std;

Uci::Uci()
{
	//engine = eng;
	if(!ChessEngine.debugMode)
		cout << ChessEngine.GetEngineInfo() << endl;
}


Uci::~Uci(void)
{
}

int Uci::loop(){

	string cmd, token;

	while (token != "quit")
	{
		//Threads.DisplayStatus();
		if (!getline(cin, cmd)) // Block here waiting for input
			cmd = "quit";

		istringstream is(cmd);

		is >> skipws >> token;

		if (token == "quit" || token == "stop"){
			ChessEngine.Stop();
			int tobeimplemented = 1;
		}
		else if (token == "ponderhit")
		{
			//		  // TODO: implement PonderMode
			//
			//          // The opponent has played the expected move. GUI sends "ponderhit" if
			//          // we were told to ponder on the same move the opponent has played. We
			//          // should continue searching but switching from pondering to normal search.
			//		  //Search::Limits.ponder = false;
			//		  //ChessEngine.SetPonderLimit(false);
			//		  //  Search::Signals.stopOnPonderhit)
			//          //if (ChessEngine.IsStopOnPonderHit())
			//			  //ChessEngine.StopThinking();
			//			  //ChessEngine.Stop();
			int tobeimplemented = 1;
		}
		else if (token == "go") {
			go(is);
			int tobeimplemented = 1;
		}
		else if (token == "ucinewgame"){
			//pos.from_fen(StartFEN, false);
			ChessEngine.SetStartPosition();
			int tobeimplemented = 1;
		}
		else if (token == "isready")
			cout << "readyok" << endl;

		else if (token == "position")
		{
			set_position(is);
		}
		else if (token == "setoption")
		{
			//set_option(is);
			int tobeimplemented = 1;
		}
		
		else if (token == "perft")
		{
			//          perft(pos, is);
			int tobeimplemented = 1;
		}
        else if (token == "d")
		{
		//		  pos.DebugDisplay();
			int tobeimplemented = 1;
		}
		
		else if (token == "flip")
		{
		//          // TODO: implement this method
		//		  int toBeImplemented=1;
		//		  //pos.flip_me();
		//		  //ChessEngine.FlipBoard();
			int tobeimplemented = 1;
		}		  
		
		else if (token == "eval")
		{
		//		  // TODO: implement this method
		//		  int toBeImplemented=1;
		//          //read_evaluation_uci_options(pos.side_to_move());
		//          //ChessEngine.ReadEvaluation();
		//		  
		//		  //cout << trace_evaluate(pos) << endl;
		//		  //ChessEngine.TraceEvaluate();
			int tobeimplemented = 1;
		}
		
		else if (token == "key")
		{
			//          cout << "key: " << hex     << pos.key()
		//               << "\nmaterial key: " << pos.material_key()
		//               << "\npawn key: "     << pos.pawn_key() << endl;
			int tobeimplemented = 1;
		}
		
		else if (token == "uci"){
			cout << ChessEngine.GetEngineInfo() << "\n"
				<< ChessEngine.GetEngineOptions()
				<< "\nuciok"      << endl;
		}
		else if ( token == "test" )
		{
			DebugTest test;
			test.LaunchTests();
		}
		else if ( token == "bench" )
		{
			DebugBench bench;
			bench.LaunchBench();
		}
		else
			cout << "Unknown command: " << cmd << endl;
	}
	return 0;
}


  const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";

void Uci::set_position(istringstream& is) {

    Move m;
    string token, fen;

    is >> token;

    if (token == "startpos")
    {
        fen = StartFEN;
        is >> token; // Consume "moves" token if any
    }
    else if (token == "fen")
        while (is >> token && token != "moves")
            fen += token + " ";
    else
        return;

	//pos.from_fen(fen, Options["UCI_Chess960"]);
	ChessEngine.SetPosition(fen);

    // Parse move list (if any)
	//while (is >> token && (m = string_to_move(ChessEngine.currentPosition, token)) != MOVE_NONE)
	while (is >> token && (m = string_to_move(ChessEngine.currentPosition, token)) != MOVE_NONE)
    {
		//TODO: take current position in consideration for stringToMove method (cPiece and cColor are missing in generated move)
		//m = move_from_uci(pos, token);
		//m = stringToMove(token);

        //pos.do_move(m, *SetupState);
		//ChessEngine.DoMove(m, *SetupState);
		ChessEngine.Play(m);
		
		/*
        // Increment pointer to StateRingBuf circular buffer
        if (++SetupState - StateRingBuf >= 102)
            SetupState = StateRingBuf;
			*/
    }
  }



  // set_option() is called when engine receives the "setoption" UCI command. The
  // function updates the UCI option ("name") to the given value ("value").

void Uci::set_option(istringstream& is) {

    string token, name, value;

    is >> token; // Consume "name" token

    // Read option name (can contain spaces)
    while (is >> token && token != "value")
        name += string(" ", !name.empty()) + token;

    // Read option value (can contain spaces)
    while (is >> token)
        value += string(" ", !value.empty()) + token;
/*
	//if (!Options.size()(name))
    if (!ChessEngine.IsValidOption(name))
        cout << "No such option: " << name << endl;

    else if (value.empty()) // UCI buttons don't have a value
        Options[name] = true;

    else
        Options[name] = value;
		
	else */
		ChessEngine.SetOption(name,value);
  }

  // go() is called when engine receives the "go" UCI command. The function sets
  // the thinking time and other parameters from the input string, and then starts
  // the main searching thread.

void Uci::go(istringstream& is) {

    string token;
    //LimitsType limits;
    std::set<Move> searchMoves;
    int time[] = { 0, 0 }, inc[] = { 0, 0 };

    while (is >> token)
    {
		if (token == "infinite") {
            //ChessEngine.SetLimitsInfinite(true);//limits.infinite = true;
			ChessEngine.brainMode = INFINITE_MODE;
		}
		else if (token == "ponder") {
            //ChessEngine.SetLimitsPonder(true);//limits.ponder = true;
			ChessEngine.SetPonderMode();
		}
        else if (token == "wtime"){
            is >> time[WHITE];
			//is >> ChessEngine.wtime;
		}
        else if (token == "btime"){
            is >> time[BLACK];
			//is >> ChessEngine.btime;
		}
        else if (token == "winc"){
            is >> inc[WHITE];
			//is >> ChessEngine.winc;
		}
        else if (token == "binc"){
            is >> inc[BLACK];
			//is >> ChessEngine.binc;
		}
        else if (token == "movestogo"){
            //is >> limits.movestogo;
			int movesToGo;
			is >> movesToGo;
			ChessEngine.SetMovestogo(movesToGo);
		}
        else if (token == "depth"){
			//is >> limits.depth;
			//ChessEngine.brainMode = DEPTH_MODE;
			int depth;
			is >> depth;
			ChessEngine.SetDepthMode(depth);
		}
        else if (token == "nodes"){
            //is >> limits.maxNodes;
			//ChessEngine.brainMode = NODES_MODE;
			int maxNodes;
			is >> maxNodes;
			ChessEngine.SetNodesMode(maxNodes);
		}
        else if (token == "movetime"){
            //is >> limits.maxTime;
			//is >> ChessEngine.thinkDelay;
			//ChessEngine.brainMode = MOVETIME_MODE;
			int movetime;
			is >> movetime;
			ChessEngine.SetMovetimeMode(movetime);
		}
        else if (token == "searchmoves"){
            // TODO: refactor this part
			//while (is >> token)
            //    searchMoves.insert(move_from_uci(pos, token));
		}
    }

	//limits.time = time[pos.side_to_move()    pos.turn?0:1];
    //limits.increment = inc[pos.side_to_move()    pos.turn?0:1];

	
    //Threads.start_thinking(pos, limits, searchMoves, true);
	//ChessEngine.StartThinking(pos, limits, searchMoves, true);
	ChessEngine.Start();
	
  }

  // perft() is called when engine receives the "perft" command. The function
  // calls perft() with the required search depth then prints counted leaf nodes
  // and elapsed time.

void Uci::perft(istringstream& is) {

	/*
    int depth, time;

    if (!(is >> depth))
        return;

	// TODO: use watch (to be improved)
	Watch w;
	w.Start();
    //time = system_time();

    //int64_t n = Search::perft(pos, depth * ONE_PLY); long ???
	long long n = perft(pos, depth * ONE_PLY);

	// TODO: use watch
	w.Stop();
	time = w.GetElapsedTime();
    //time = system_time() - time;

    std::cout << "\nNodes " << n
              << "\nTime (ms) " << time
              << "\nNodes/second " << int(n / (time / 1000.0)) << std::endl;
			  */
}


///// Search::perft() is our utility to verify move generation. All the leaf nodes
///// up to the given depth are generated and counted and the sum returned.
//
////int64_t
//long long perft(Position& pos, int depth) {
//
//  //StateInfo st;
//  //int64_t cnt = 0;
//  long long cnt = 0;
//  /*
//
//  Movevector<MV_LEGAL> ml(pos);
//
//  // At the last ply just return the number of moves (leaf nodes)
//  if (depth == ONE_PLY)
//      return ml.size();
//
//  CheckInfo ci(pos);
//  for ( ; !ml.end(); ++ml)
//  {
//      pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci));
//      cnt += perft(pos, depth - ONE_PLY);
//      pos.undo_move(ml.move());
//  }
//  */
//  return cnt;
//}

/*
/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent Move if any.
/// Moves are guaranteed to be legal.
Move Uci::move_from_uci(const Position& pos, const string& str) {
  for (Movevector<MV_LEGAL> ml(pos); !ml.end(); ++ml)
      if (str == move_to_uci(ml.move(), pos.is_chess960()))
          return ml.move();
	return MOVE_NONE;


  return NULL;
}
*/

///*
//int Uci::system_time() {
//  struct _timeb t;
//  _ftime(&t);
//  return int(t.time * 1000 + t.millitm);
//}
//*/

void Uci::bench(std::istringstream &is){

    string partToBench;

    if (!(is >> partToBench))
        return;
	//ChessEngine.Bench(partToBench);
}

void Uci::test(std::istringstream &is){

    string partToTest;

    if (!(is >> partToTest))
        return;
	//ChessEngine.Test(partToTest);
}
